package com.bolt.auth.security.admin.biz.sys.service;


import com.bolt.auth.admin.api.sys.dto.MenuDTO;
import com.bolt.auth.admin.api.sys.dto.MenuPrivilegeDTO;
import com.bolt.auth.admin.api.sys.dto.MenuQueryCriteria;
import com.bolt.auth.admin.api.sys.dto.converter.MenuConverter;
import com.bolt.auth.admin.api.sys.entity.MenuEntity;
import com.bolt.auth.admin.api.sys.entity.PrivilegeEntity;
import com.bolt.auth.admin.api.sys.entity.RoleEntity;
import com.bolt.auth.admin.api.sys.vo.MenuMetaVO;
import com.bolt.auth.admin.api.sys.vo.MenuVO;
import com.bolt.auth.security.admin.biz.sys.repository.MenuRepository;
import com.bolt.auth.security.common.exception.BizExceptionEnum;
import com.bolt.common.collection.CollectionUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.convention.exception.ServiceException;
import com.bolt.support.base.service.BaseTreeService;
import com.bolt.support.spring.jpa.filter.FilterBuilder;
import com.bolt.support.spring.jpa.jpql.Clauses;
import com.bolt.support.spring.jpa.jpql.CompareClause;
import com.bolt.support.spring.jpa.repository.BasicJpaRepository;
import com.bolt.support.spring.jpa.specification.QueryHelp;
import com.bolt.support.ui.tree.vo.UITreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统菜单管理
 *
 * @author michao
 *         Created by Administrator on 2017/8/7.
 */
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class MenuService extends BaseTreeService <MenuEntity, Long> {

    @Autowired
    private MenuRepository menuRepository;

    @Autowired
    private PrivilegeService privilegeService;

    @Autowired
    private RoleService roleService;


    @Override
    protected BasicJpaRepository <MenuEntity, Long> getRepository() {
        return menuRepository;
    }


    @Transactional(rollbackFor = Exception.class)
    public MenuEntity saveOrUpdate(MenuDTO menuDTO) {
        MenuEntity entity = MenuConverter.toEntity(menuDTO);
        return save(entity);
    }

    public List <MenuEntity> getMenus(Long pid) {
        List <MenuEntity> menus;
        if (pid != null && !pid.equals(0L)) {
            menus = findChildren(pid);
        } else {
            menus = menuRepository.findByPidIsNull();
        }
        return menus;
    }

    public List <MenuDTO> findByUser(String currentUserId) {
        List <RoleEntity> roleEntities = roleService.findRoles(currentUserId);
        Set <String> roleIds = roleEntities.stream().map(RoleEntity::getId).collect(Collectors.toSet());
        List <PrivilegeEntity> privilegeEntities = privilegeService.findPrivileges(roleIds);
        Set <Long> menuIds = privilegeEntities.stream().map(PrivilegeEntity::getMenuId).collect(Collectors.toSet());
        List <MenuDTO> menuDtos = findAllMenus(menuIds);
        return menuDtos;
    }

    public List <MenuDTO> findAllMenus() {
        List <MenuEntity> menus = menuRepository.findAll(Sort.by(Sort.Direction.ASC,"orderRank"));
        List <MenuDTO> menuDtos = MenuConverter.toDTO(menus);
        return menuDtos;
    }

    public List <MenuDTO> findAllMenus(Set<Long> menuIds ) {
        List <MenuEntity> menus = this.findAll((FilterBuilder<MenuEntity> f) ->
                f.lambda().in(MenuEntity::getId, menuIds).orderByAsc(MenuEntity::getOrderRank).build());
        List <MenuDTO> menuDtos = MenuConverter.toDTO(menus);
        return menuDtos;
    }

    @Override
    protected void converterTreeNode(MenuEntity entity, UITreeNode treeNode) {
        super.converterTreeNode(entity, treeNode);
        treeNode.setName(entity.getTitle());
    }

    public List <MenuPrivilegeDTO> getMenuPrivilege(String id) {
        CompareClause compareClause;
        if (StrUtil.isBlank(id)) {
            compareClause = Clauses.of().isNull(Clauses.of(MenuEntity::getPid));
        } else {
            PrivilegeEntity privilegeEntity =  privilegeService.findOne(id)
                    .orElseThrow(() -> new ServiceException(BizExceptionEnum.DATA_NOT_FIND));
            compareClause = Clauses.of().eq(Clauses.of(MenuEntity::getPid), privilegeEntity.getMenuId());
        }
        compareClause.and( Clauses.of().eq(Clauses.of(MenuEntity::getHidden),Boolean.FALSE));
        compareClause.and( Clauses.of().eq(Clauses.of(MenuEntity::getEnabled),Boolean.TRUE));
        return this.jpaQueryFactory.select(MenuPrivilegeDTO.class,
                Clauses.of(MenuEntity::getId).as("menuId"),
                Clauses.of(PrivilegeEntity::getId),
                Clauses.of(PrivilegeEntity::getTitle),
                Clauses.of(PrivilegeEntity::getCode),
                Clauses.of(MenuEntity::getPid),
                Clauses.of(MenuEntity::getChildrenSize),
                Clauses.of(MenuEntity::getLevel),
                Clauses.of(PrivilegeEntity::getType),
                Clauses.of(PrivilegeEntity::getChildrenSize).as("subPrivilegeSize")
        ).from(Clauses.of(MenuEntity.class))
                .leftJoin(Clauses.of(PrivilegeEntity.class))
                .on(Clauses.of(MenuEntity::getId), Clauses.of(PrivilegeEntity::getMenuId))
                .where(compareClause)
                .orderBy(Clauses.of(MenuEntity::getOrderRank))
                .fetch();
    }

    public List <MenuDTO> buildTreeDto(List <MenuDTO> menuDtos) {
        List <MenuDTO> trees = new ArrayList <>();
        Set <Long> ids = new HashSet <>();
        for (MenuDTO menuDTO : menuDtos) {
            if (menuDTO.getPid() == null) {
                trees.add(menuDTO);
            }
            for (MenuDTO it : menuDtos) {
                if (menuDTO.getId().equals(it.getPid())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList <>());
                    }
                    menuDTO.getChildren().add(it);
                    ids.add(it.getId());
                }
            }
        }
        if (trees.size() == 0) {
            trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        }
        return trees;
    }

    public List <MenuVO> buildMenus(List <MenuDTO> menuDtos) {
        List <MenuVO> list = new LinkedList <>();
        menuDtos.forEach(menuDTO -> {
                    if (menuDTO != null) {
                        List <MenuDTO> menuDtoList = menuDTO.getChildren();
                        MenuVO menuVo = new MenuVO();
                        menuVo.setName(StrUtil.isNotBlank(menuDTO.getComponentName()) ? menuDTO.getComponentName() : menuDTO.getTitle());
                        // 一级目录需要加斜杠，不然会报警告
                        menuVo.setPath(menuDTO.getPid() == null ? "/" + menuDTO.getUrl() : menuDTO.getUrl());
                        menuVo.setHidden(menuDTO.getHidden());
                        // 如果不是外链
                        if (!menuDTO.getIFrame()) {
                            if (menuDTO.getPid() == null) {
                                menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent()) ? "Layout" : menuDTO.getComponent());
                                // 如果不是一级菜单，并且菜单类型为目录，则代表是多级菜单
                            } else if (StrUtil.isNotBlank(menuDTO.getComponent())) {
                                menuVo.setComponent(menuDTO.getComponent());
                            }
                        }
                        menuVo.setMeta(new MenuMetaVO(menuDTO.getTitle(), menuDTO.getIcon(), !menuDTO.getCache()));
                        if (CollectionUtil.isNotEmpty(menuDtoList)) {
                            menuVo.setAlwaysShow(true);
                            menuVo.setRedirect("noredirect");
                            menuVo.setChildren(buildMenus(menuDtoList));
                            // 处理是一级菜单并且没有子菜单的情况
                        } else if (menuDTO.getPid() == null) {
                            MenuVO menuVo1 = new MenuVO();
                            menuVo1.setMeta(menuVo.getMeta());
                            // 非外链
                            if (!menuDTO.getIFrame()) {
                                menuVo1.setPath("index");
                                menuVo1.setName(menuVo.getName());
                                menuVo1.setComponent(menuVo.getComponent());
                            } else {
                                menuVo1.setPath(menuDTO.getUrl());
                            }
                            menuVo.setName(null);
                            menuVo.setMeta(null);
                            menuVo.setComponent("Layout");
                            List <MenuVO> list1 = new ArrayList <>();
                            list1.add(menuVo1);
                            menuVo.setChildren(list1);
                        }
                        list.add(menuVo);
                    }
                }
        );
        return list;
    }

    /**
     * 根据菜单重构权限资源配置表
     */
    @Transactional(rollbackFor = Exception.class)
    public void impMenu2Privilege(Long[] menuId, boolean haveButton) {
        List <MenuEntity> menus = new ArrayList();
        if (menuId == null) {
            List <PrivilegeEntity> privileges = privilegeService.findAllCached();
            privilegeService.delete(privileges);
            menus = menuRepository.findAllChildrenMenu();
        } else {
            List <PrivilegeEntity> privileges = privilegeService.findPrivilegeByMenuId(menuId);
            privilegeService.deleteAll(privileges);
            menus = menuRepository.findAllByIds(menuId);
        }
        for (MenuEntity menu : menus) {
            if(menu.getHidden()){
                continue;
            }
            String url = menu.getComponent();
            PrivilegeEntity privilege = new PrivilegeEntity();
            if(StrUtil.isNotBlank(url)){
                url = StrUtil.subBefore(url,"/index",true);
                privilege.setCode(url.replace('/', ':'));
            }
            privilege.setMenuId(menu.getId());
            privilege.setType(PrivilegeEntity.PrivilegeTypeEnum.URL);
            privilege.setOrderRank(0);
            privilege.setLevel(0);
            privilege.setChildrenSize(0);
            privilege.setEnabled(true);
           if(null != menu.getParent() && null != menu.getParent().getId()){
               PrivilegeEntity entity =   privilegeService.findUrlPrivilege(menu.getParent().getId());
               if(null != entity){
                   privilege.setParent(new PrivilegeEntity(entity.getId()));
               }
           }
            privilege.setTitle(menu.getTitle());
            privilege = privilegeService.save(privilege);

            if (haveButton && menu.getChildrenSize() == 0) {
                //默认追加增删除三个按钮
                PrivilegeEntity add = new PrivilegeEntity();
                add.setCode(privilege.getCode() + ",create");
                add.setMenuId(menu.getId());
                add.setType(PrivilegeEntity.PrivilegeTypeEnum.BUTTON);
                add.setParent(new PrivilegeEntity(privilege.getId()));
                add.setOrderRank(1);
                add.setEnabled(true);
                add.setTitle("新增");
                privilegeService.save(add);


                PrivilegeEntity update = new PrivilegeEntity();
                update.setCode(privilege.getCode() + ",edit");
                update.setMenuId(menu.getId());
                update.setType(PrivilegeEntity.PrivilegeTypeEnum.BUTTON);
                update.setOrderRank(2);
                update.setEnabled(true);
                update.setParent(new PrivilegeEntity(privilege.getId()));
                update.setTitle("编辑");
                privilegeService.save(update);


                PrivilegeEntity del = new PrivilegeEntity();
                del.setCode(privilege.getCode() + ",del");
                del.setMenuId(menu.getId());
                del.setType(PrivilegeEntity.PrivilegeTypeEnum.BUTTON);
                del.setOrderRank(3);
                del.setEnabled(true);
                del.setParent(new PrivilegeEntity(privilege.getId()));
                del.setTitle("删除");
                privilegeService.save(del);
            }
        }
    }

    /**
     * 删除操作
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(MenuEntity entity) {
        //同步清理自动创建的对应权限数据
        if (entity.getParent() != null && entity.getParent() != null) {
            MenuEntity parent = menuRepository.getOne(entity.getParent().getId());
        }
        super.delete(entity);
    }

    public List <MenuEntity> queryMenus(MenuQueryCriteria criteria) {
        Sort sort = Sort.by(Sort.Direction.ASC, "orderRank");
        return menuRepository.findAll(
                (root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), sort);
    }

    public MenuDTO findById(long id) {
        MenuEntity menu = menuRepository.findById(id).orElseGet(MenuEntity::new);
        return MenuConverter.toDTO(menu);
    }



}
